package sf.dsl.example;

import sf.database.dialect.DBDialect;
import sf.database.jdbc.sql.SQLParameter;
import sf.tools.StringUtils;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public class WindowFunction extends SimpleField {
    public static final String ORDER_BY = "order by ";

    public static final String PARTITION_BY = "partition by ";

    private final List<ICondition> filter = new LinkedList<>();

    private final List<OrderField> orderBy = new LinkedList<>();

    private final List<SimpleField> partitionBy = new LinkedList<>();

    private final SimpleField target;

    /**
     * WINDOW window_name AS (window_spec)
     * [, window_name AS (window_spec)] ...
     * 可以为空
     */
    protected String windowName;

    private String rowsOrRange;

    public WindowFunction(SimpleField target) {
        this.target = target;
    }

    @Override
    public void toConditionSql(StringBuilder builder, List<SQLParameter> list, AtomicBoolean useDefaultTableNameAlias, boolean useTablePrefix, DBDialect dialect) {
        target.toConditionSql(builder, list, useDefaultTableNameAlias, useTablePrefix, dialect);
        if (!filter.isEmpty()) {
            builder.append(" filter (where ");
            for (ICondition condition : filter) {
                condition.toSql(builder, list, useDefaultTableNameAlias, dialect);
            }
            builder.append(')');
        }
        builder.append(" over ");
        if (StringUtils.isNotBlank(windowName)) {
            builder.append(windowName);
        } else {
            builder.append('(');
            windowDefine(builder, list, useDefaultTableNameAlias, dialect);
            builder.append(')');
        }
    }

    protected void windowDefine(StringBuilder builder, List<SQLParameter> list, AtomicBoolean useDefaultTableNameAlias, DBDialect dialect) {
        if (!partitionBy.isEmpty()) {
            builder.append(PARTITION_BY);
            boolean first = true;
            for (SimpleField expr : partitionBy) {
                if (!first) {
                    builder.append(',');
                }
                expr.toConditionSql(builder, list, useDefaultTableNameAlias, true, dialect);
                first = false;
            }
        }
        if (!orderBy.isEmpty()) {
            if (!partitionBy.isEmpty()) {
                builder.append(' ');
            }
            builder.append(ORDER_BY);
            boolean b = false;
            for (OrderField orderField : orderBy) {
                if (b) {
                    builder.append(',');
                }
                b = true;
                orderField.toConditionSql(builder, list, useDefaultTableNameAlias, true, dialect);
            }
        }
        if (rowsOrRange != null) {
            builder.append(rowsOrRange);
        }
    }

    public WindowFunction as(String alias) {
        super.as(alias);
        return this;
    }

    public WindowFunction orderBy(OrderField orderBy) {
        this.orderBy.add(orderBy);
        return this;
    }

    public WindowFunction orderBy(OrderField... orderBy) {
        Collections.addAll(this.orderBy, orderBy);
        return this;
    }

    public WindowFunction partitionBy(SimpleField partitionBy) {
        this.partitionBy.add(partitionBy);
        return this;
    }

    public WindowFunction partitionBy(SimpleField... partitionBy) {
        Collections.addAll(this.partitionBy, partitionBy);
        return this;
    }

    public WindowFunction filter(ICondition... conditions) {
        Collections.addAll(this.filter, conditions);
        return this;
    }

    public WindowFunction withRowsOrRange(String rowsOrRange) {
        this.rowsOrRange = rowsOrRange;
        return this;
    }

    public WindowRows rows() {
        return new WindowRows(this, " rows");
    }

    public WindowRows range() {
        return new WindowRows(this, " range");
    }
}
